﻿/******************************************************************************

    Copyright (c) 2010, Sergey Mudrov
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
        * Redistributions of source code must retain the above copyright
          notice, this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright
          notice, this list of conditions and the following disclaimer in the
          documentation and/or other materials provided with the distribution.
        * Neither the name of Sergey Mudrov nor the
          names of its contributors may be used to endorse or promote products
          derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL SERGEY MUDROV BE LIABLE FOR ANY
    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*******************************************************************************/

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;
using System.Diagnostics;
using System.Threading;
using JeffWilcox.Utilities.Silverlight;
using System.Collections.Generic;

namespace VkApi
{
    /// <summary>
    /// Class that represents VK API.
    /// </summary>
    public class Vk
    {
        #region Static members.

        private static Vk _instance = new Vk();

        /// <summary>
        /// Gets the instance of VK API.
        /// </summary>
        /// <value>The instance.</value>
        public static Vk Instance { get { return Vk._instance; } }

        #endregion

        /// <summary>
        /// Occurs when [vk init succeeded].
        /// </summary>
        public event EventHandler VkInitSucceeded;
        /// <summary>
        /// Occurs when [vk init failed].
        /// </summary>
        public event VkInitFailed_Handler VkInitFailed;

        #region Member variables.

        private readonly object _syncRoot = new object();

        private string _objectID = null;
        private long _appId = 0;
        private string _secret = null;
        private long _viewerID = 0;
        private bool _isTestMode = false;

        private ScriptObject _vk = null;
        private VkScriptable _vkScriptable = null;
        private VkExternal _vkExternal = null;
        private VkApi _vkApi = null;
        private bool _apiInitialized = false;
        private bool _xdConnectionInitialized = false;
        private InitialParameters _parameters = null;

        #endregion

        #region Properties.

        /// <summary>
        /// Application initial parameters.
        /// </summary>
        public InitialParameters Parameters
        {
            get
            {
                return this._parameters;
            }
        }

        /// <summary>
        /// Vk external API.
        /// </summary>
        public VkExternal External
        {
            get
            {
                if (this._xdConnectionInitialized == false)
                    throw new NotInitializedException("External functions failed during initialization!");

                return this._vkExternal;
            }
        }

        /// <summary>
        /// VK API.
        /// </summary>
        public VkApi Api
        {
            get
            {
                if (this._apiInitialized == false)
                    throw new NotInitializedException();

                return this._vkApi;
            }
        } 

        #endregion

        /// <summary>
        /// Constructor.
        /// </summary>
        protected Vk()
        {
            // Get parameters
            this._parameters = new InitialParameters();
        }

        /// <summary>
        /// Initialize Vk Api.
        /// </summary>
        /// <param name="objectID">Silverlight object ID in HTML document.</param>
        /// <param name="appID">Vk application ID.</param>
        /// <param name="secret">Vk application secret.</param>
        /// <param name="viewerID">Current viewer ID.</param>
        /// <param name="isTestMode">Is API in test mode.</param>
        public void Initialize(string objectID, long appID, string secret, long viewerID, bool isTestMode)
        {
            lock (this._syncRoot)
            {
                if (this._apiInitialized)
                    return;

                try
                {
                    this._objectID = objectID;
                    this._appId = appID;
                    this._secret = secret;
                    this._viewerID = viewerID;
                    this._isTestMode = isTestMode;
                    

                    // init scripts
                    this._vkScriptable = new VkScriptable(this._objectID);
                    this._vkScriptable.InitOk += new EventHandler(VkScriptable_InitOk);
                    this._vkScriptable.InitFailed += new EventHandler(VkScriptable_InitFailed);

                    // Get init script     ==========================================
#if DEBUG
                    Trace.WriteLine("Vk: Try get js VK Init function..."); 
#endif

                    ScriptObject script = HtmlPage.Window.GetProperty("VkApi_InitVK") as ScriptObject;

                    if (script == null)
                    {
#if DEBUG
                        Trace.WriteLine("Vk: VK Init function not found"); 
#endif

                        throw new Exception("VK Init function not found.");
                    }

#if DEBUG
                    Trace.WriteLine("Vk: VK Init function found"); 
#endif

                    // Run init script     ==========================================
#if DEBUG
                    Trace.WriteLine("Vk: Try run VK Init function..."); 
#endif

                    try
                    {
                        script.InvokeSelf();

                        this._xdConnectionInitialized = true;

                        Trace.WriteLine("Vk: VK Init function succeeded");
                    }
                    catch
                    {
                        Trace.WriteLine("Vk: VK Init function failed! Start backup init...");

                        this._vkScriptable.InsertBackupVK();

                        script.InvokeSelf();

                        this._xdConnectionInitialized = true;

                        Trace.WriteLine("Vk: Backup VK Init succeeded");
                    }

                    // Find VK         ==========================================
#if DEBUG
                    Trace.WriteLine("Vk: Try get VK js object...");
#endif

                    this._vk = this.TryGetVkObject();

                    if (this._vk == null)
                    {
#if DEBUG
                        Trace.WriteLine("Vk: VK js object not found.");
#endif

                        throw new Exception("VK js object not found.");
                    }

#if DEBUG
                    Trace.WriteLine("Vk: VK js object found");
#endif

                    // Create VkExternal & VkApi       ==========================================
                    this._vkExternal = new VkExternal(this._vk, this._vkScriptable);
                    this._vkApi = new VkApi(this._vkScriptable, this._appId, this._secret, this._viewerID, this._isTestMode);

                    this._apiInitialized = true;

#if DEBUG
                    Trace.WriteLine("Vk: Init succeeded"); 
#endif
                }
                catch (Exception ex)
                {
                    this.OnVkInitFailed(ex);
                }
            }
        }

        #region Internal implementation.

        /// <summary>
        /// 
        /// </summary>
        private void VkScriptable_InitOk(object sender, EventArgs e)
        {
            this.OnVkInitSucceeded();
        }

        /// <summary>
        /// 
        /// </summary>
        private void VkScriptable_InitFailed(object sender, EventArgs e)
        {
            this.OnVkInitFailed(new Exception("Vk initialization script failed"));
        }

        /// <summary>
        /// 
        /// </summary>
        private void OnVkInitFailed(Exception ex)
        {
            if (this.VkInitFailed != null)
                this.VkInitFailed(this, new VkInitFailedEventArgs(ex));
        }

        /// <summary>
        /// 
        /// </summary>
        private void OnVkInitSucceeded()
        {
            if (this.VkInitSucceeded != null)
                this.VkInitSucceeded(this, EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        private ScriptObject TryGetVkObject()
        {
            // NOTE:   xd_connection.js file can be stiil downloading by the time this code is executed.
            //         So we give it 5 seconds chance to accomplish this operation :)
            ScriptObject s = null;
            int i = 0;

            for (; i < 25; i++)
            {
                s = HtmlPage.Window.GetProperty("VK") as ScriptObject;

                if (s != null)
                    break;

                Thread.Sleep(200);
            }

#if DEBUG
            Trace.WriteLine(String.Format("VK object has been found within {0} attempts", i + 1)); 
#endif

            return s;
        }

        #endregion
    }
}
